/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simulation;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author haoliang.donghl
 */
public class SocketMessage {
    private static SocketMessage instanMain = null;
    private static final boolean printMsg = false;
    private int machineNum = 3;
    private int appNum = 20;

    private SocketMessage(){
        init();
    }

    public static SocketMessage getInstance(){
        if (instanMain == null){
            instanMain = new SocketMessage();
        }
        return instanMain;
    }

    public String receiveErlangData(DataInputStream in) {
        try {
            StringBuffer sb = new StringBuffer("");
            char lastChar;
            byte b;
            do {
                b = in.readByte();
                lastChar = (char) b;
                if (lastChar == '\0') {
                    b = in.readByte();
                    b = in.readByte();
                    b = in.readByte();
                }
                if (lastChar != '\r' && lastChar != '\n' && lastChar != '\0') sb.append(lastChar);
            } while (lastChar != '\n');
            if (printMsg){
                System.out.print("RECV: ");
                System.out.println(sb);
            }
            return sb.toString();
        } catch (IOException ex) {
            Logger.getLogger(SocketMessage.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public int packetErlangData(String text, byte[] result) {
        int l = text.length();
        result[0] = '\0';
        result[1] = '\0';
        result[2] = '\0';
        result[3] = (byte) l;
        for (int i = 4; i < l + 4; i++) {
            result[i] = (byte) text.charAt(i - 4);
        }
        return l + 4;
    }

    public void sendErlangData(DataOutputStream out, String text) {
        if (printMsg){
            System.out.print("SEND: ");
            System.out.println(text);
        }
        try {
            byte[] content = new byte[text.length() + 6];
            packetErlangData(text, content);
            content[content.length - 2] = 13;
            content[content.length - 1] = 10;
            out.write(content);
            out.flush();
        } catch (IOException ex) {
            Logger.getLogger(SocketMessage.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public String getParameter(String data, String name){
        String[] splited = data.split("&");
        for (int i = 0; i < splited.length; i++){
            String[] value = splited[i].split("=");
            if (name.equals(value[0])) return value[1];
        }
        return null;
    }

    public void connectAlgorithm() {
        try {
            Socket socket = new Socket("127.0.0.1", 2345);
            DataInputStream in = new DataInputStream(socket.getInputStream());
            DataOutputStream out = new DataOutputStream(socket.getOutputStream());
            sendErlangData(out, "msgtype=1&machinenum=" + Machine.getMachineNum()
                              + "&appnum=" + Application.getAppNum());
            receiveErlangData(in);
            int recvTimes = 0;
            while (recvTimes < Application.getAppNum()){
                String recv = receiveErlangData(in);
                String appid = getParameter(recv, "appid");
                Application app = Application.getAppById(Integer.parseInt(appid));
                StringBuffer sb = new StringBuffer("");
                sb.append("msgtype=2&appid=");
                sb.append(app.getId());
                sb.append("&appname=");
                sb.append(app.getName());
                sb.append("&machinenum=");
                sb.append(Machine.getMachineNum());
                Instance[] instances = app.getInstances();
                for (int i = 0; i < instances.length; i++){
                    sb.append("&machinename=");
                    sb.append(instances[i].getMachine().getName());
                    sb.append("&state=");
                    sb.append(instances[i].state);
                    sb.append("&load=");
                    if (i == 0){
                        sb.append((int)(app.getLoad() * 10000));
                    }else{
                        sb.append(0);
                    }
                }
                sb.append("&response=ok");
                sendErlangData(out, sb.toString());
                recvTimes++;
            }

            receiveErlangData(in);
            sendErlangData(out, "msgtype=3&response=ok");
            for (int i = 0; i < Application.getAppNum(); i++){
                Instance[] instances = Application.getAppById(i + 1).getInstances();

                for (int j = 0; j < 3; j++){
                    String str = "msgtype=4&appname=" + Application.getAppById(i + 1).getName() + "&state=" + j;
                    sendErlangData(out, str);
                    str = receiveErlangData(in);
                    str = str.split("%")[1];
                    String[] strMachines = str.split("#");
                    for (int k = 0; k < strMachines.length; k++){
                        for (int l = 0; l < instances.length; l++){
                            if (instances[l].getMachine().getName().equals(strMachines[k])){
                                instances[l].setState(j);
                            }
                        }
                    }
                }
            }

            socket.close();
        } catch (UnknownHostException ex) {
            Logger.getLogger(SocketMessage.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(SocketMessage.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private void init() {
        for (int i = 0; i < machineNum; i++) {
            new Machine();
        }
        for (int i = 0; i < appNum; i++) {
            new Application("app" + String.valueOf(i + 1));
        }
    }
}
